package com.hzit.filter;

import cn.hutool.core.map.TolerantMap;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.alibaba.fastjson.JSON;
import com.hzit.common.constants.TokenConstants;
import com.hzit.common.entity.LoginUser;
import com.hzit.common.util.JwtUtil;
import com.hzit.common.util.R;
import com.hzit.common.util.ServletUtils;
import com.hzit.config.WhiteListProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.SignatureException;
import jdk.nashorn.internal.parser.Token;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;

/**
 * 类名：
 * 作者：WF
 * 认证功能：
 *   1. 认证时，前端发送请求都会携带一个名为： Authroziation的请求头，值为：Bearer token值
 *   2. 经过网关，对token值解析，得到uuid，从而在redis中得到登录用户
 *   3、判断是否在redis中有对应的key，如果有证明认证成功，否则，认证失败！
 *   4、如果在网关层出现了异常，则由网关的全局异常处理进行处理！
 * 授权功能：
 *   1. 可以定义一个自定义注解
 *   2. 通过aop的环绕通知，在方法前添加注解，从而得到一个权限串
 *   3. 每次发送请求到方法上都会经过aop的通知进行判断是否有对应的权限，（判断依据：可以根据当前登录时得到的用户权限串列表及在注解上定义的权限串比较）
 *
 */
@Component
@Slf4j
public class AuthFilter implements Ordered, GlobalFilter {
	@Autowired
	private WhiteListProperties whiteListProperties;        // 得到白名单列表
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		//1. 得到请求对象
		ServerHttpRequest request = exchange.getRequest();
		//2. 得到请求路径
		String path = request.getURI().getPath();
		//3. 判断是否在放行的白名单中,如果在就放行
		List<String> whitelist = whiteListProperties.getWhitelist();
		if(whitelist.contains(path)){
			return chain.filter(exchange);
		}
		//4. 得到请求头中的token
		String token = getToken(request);
		if(StrUtil.isBlank(token)){
			return ServletUtils.webFluxResponseWriter(exchange.getResponse(),"认证不成功！", HttpStatus.UNAUTHORIZED.value());

		}
		//5. 解析token值
		Claims claims = JwtUtil.parseToken(token);
		if(claims == null){
			return ServletUtils.webFluxResponseWriter(exchange.getResponse(),"Token信息丢失或无效！", HttpStatus.UNAUTHORIZED.value());
		}

		//得到经过jwt解密后的uuid的值
		String  uuid_token = claims.get(TokenConstants.USER_KEY).toString();
		//6. 拼凑redis的key
		String key = getKey(uuid_token);
		//7. 根据key得到值(登录用户的字符串形式)
		String loginUserInfo = redisTemplate.opsForValue().get(key);
		if(StrUtil.isBlank(loginUserInfo)){
			return ServletUtils.webFluxResponseWriter(exchange.getResponse(),"登录用户无效！", HttpStatus.UNAUTHORIZED.value());
		}
		log.info("全局网关开始工作了！");

		return chain.filter(exchange);     // 放行;
	}

	// redis的key
	private String getKey(String uuid_token) {
		return TokenConstants.LOGIN_USER_REDIS_KEY + uuid_token;
	}

	// 在请求头中获取token
	private String getToken(ServerHttpRequest request) {
		String token = request.getHeaders().getFirst(TokenConstants.LOGIN_AUTH_HEADER);
		if(StrUtil.isNotBlank(token)){
			if(token.startsWith(TokenConstants.LOGIN_AUTH_BEARER)){
				return token.replaceAll(TokenConstants.LOGIN_AUTH_BEARER,"");
			}
		}
		return null;
	}

	//5. 定义将指定的信息输出到页面中
	private Mono<Void> webFluxResponseWriter(ServerWebExchange exchange, String msg,int code) {
		ServerHttpResponse response = exchange.getResponse();
		R fail = R.fail(code, msg);
		DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(fail).getBytes());
		return response.writeWith(Mono.just(buffer));
	}
	@Override
	public int getOrder() {
		return 0;
	}
}
